package net.p2pcdn.core.order.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qc.bean.result.Paging;
import com.qc.bean.result.ResponseData;
import net.p2pcdn.core.order.command.ContractQueryCommand;
import net.p2pcdn.core.order.command.QueryTrafficCommand;
import net.p2pcdn.core.order.domain.*;
import net.p2pcdn.core.order.repository.*;
import net.p2pcdn.core.product.domain.Product;
import net.p2pcdn.core.product.service.ProductService;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ContractService {
    @Resource
    private ContractRepository contractRepository;
    @Resource
    private CDNDomainRepository cdnDomainRepository;
    @Resource
    private ProductService productService;
    @Resource
    private OrderDetailRepository orderDetailRepository;
    @Resource
    private TrafficUpdateRecordRepository trafficUpdateRecordRepository;
    @Resource
    private OrderRepository orderRepository;
    @Autowired
    private RedissonClient redissonClient;


    public List<Contract> findContracts(int userId, Integer productId, Boolean expired, Integer status) {
        Map<String, Object> params = new HashMap<>(8);
        params.put("userId", userId);
        params.put("productId", productId);
        params.put("expired", expired);
        params.put("status", status);
        return contractRepository.findEffectiveContract(params);
    }

    public Optional<Contract> findLastUnEffectiveContract(int userId, int productId) {
        List<Contract> contracts = findContracts(userId, productId, false, Contract.EFFECTIVE);
        if (contracts.size() == 0) {
            return Optional.empty();
        }
        Contract lastContract = contractRepository.findLastUnEffectiveContract(userId, productId);
        if (lastContract != null) {
            return Optional.of(lastContract);
        }
        Contract contract = contracts.get(0);
        if (contract.getExpireTime().compareTo(new Date()) > 0) {
            return Optional.of(contract);
        } else {
            return Optional.empty();
        }
    }

    public void saveContract(List<Contract> contracts, TrafficUpdateRecord.ChangeType changeType) {
        Contract contract = contracts.get(0);
        ResponseData responseData = this.doesDomainBundled(contract.getUserId(), contract.getProductId(), contract.resolveFormattedDomains());
        if (!responseData.isSuccess()) {
            throw new RuntimeException(responseData.getMessage());
        }
        this.contractRepository.batchInsert(contracts);
        List<CDNDomain> domains = new LinkedList<>();
        List<TrafficUpdateRecord> updateRecords = new LinkedList<>();
        contracts.forEach(item -> {
            item.buildCdnDomains();
            domains.addAll(item.getDomains());
            updateRecords.add(new TrafficUpdateRecord(item.getId(), item.getCreator(), item.getStandardQuantity(), changeType, true));
        });
        this.cdnDomainRepository.batchInsert(domains);
        if (updateRecords.size() > 0) {
            this.batchAddTrafficUpdateRecord(updateRecords);
        }
    }


    /**
     * 提交订单前检查域名是否已经绑定了其他产品
     *
     * @param userId
     * @param productId
     * @param domains
     * @return
     */
    public ResponseData doesDomainBundled(int userId, final int productId, List<String> domains) {
        List<Contract> contracts = this.findContracts(userId, null, false, Contract.EFFECTIVE);
        if (contracts.isEmpty()) {
            return ResponseData.success("ok");
        }
        List<CDNDomain> existDomains = this.cdnDomainRepository.findAll(contracts.stream().map(Contract::getId).collect(Collectors.toList()));
        Map<String, Integer> table = existDomains.stream().collect(Collectors.toMap(CDNDomain::getDomain, CDNDomain::getContractId));
        table.entrySet().removeIf(item -> {
            Contract contract = this.getById(item.getValue());
            return contract.getProductId() == productId;
        });
        for (String item : domains) {
            boolean result = table.containsKey(item);
            if (result) {
                Contract contract = this.contractRepository.selectByPrimaryKey(table.get(item));
                Product product = this.productService.getProduct(contract.getProductId());
                return ResponseData.error("域名[" + item + "] 已经绑定套餐" + product.getName(), 500);
            }
        }
        return ResponseData.success("ok");
    }

    public void addTrafficUpdateRecord(int contractId, int userId, BigDecimal amount, TrafficUpdateRecord.ChangeType changeType, boolean add) {
        TrafficUpdateRecord trafficUpdateRecord = new TrafficUpdateRecord(contractId, userId, amount, changeType, add);
        this.trafficUpdateRecordRepository.insert(trafficUpdateRecord);
    }

    public void batchAddTrafficUpdateRecord(List<TrafficUpdateRecord> records) {
        this.trafficUpdateRecordRepository.batchInsert(records);
    }

    public Contract getById(Integer contractId) {
        return contractRepository.selectByPrimaryKey(contractId);
    }

    public void chargeTraffic(int userId, Contract contract, Product product, int quantity, TrafficUpdateRecord.ChangeType changeType) {
        contract.setExtraQuantity(contract.getExtraQuantity().add(product.getAmount().multiply(new BigDecimal(quantity))));
        contract.setRemainingQuantity(contract.getRemainingQuantity().add(product.getAmount().multiply(new BigDecimal(quantity))));
        contract.setModifyTime(new Date());
        contract.setModifier(userId);
        this.addTrafficUpdateRecord(contract.getId(), userId, product.getAmount(), changeType, true);
    }

    public void consumeTraffic(int userId, int contractId, BigDecimal amount, TrafficUpdateRecord.ChangeType changeType) {
        Contract contract = this.getById(contractId);
        if (contract.isExpired()) {
            throw new RuntimeException("服务已过期不能赠送流量");
        }
        if (userId != -1) {
            contract.setModifier(userId);
        }
        contract.setModifyTime(new Date());
        BigDecimal value = amount.negate();
        contract.setRemainingQuantity(contract.getRemainingQuantity().add(value));
        this.contractRepository.updateByPrimaryKey(contract);
        this.addTrafficUpdateRecord(contractId, userId, value, changeType, value.compareTo(new BigDecimal("0")) > 0);
    }

    public Paging<ContractVO> queryContracts(ContractQueryCommand contractQueryCommand) {
        PageHelper.startPage(contractQueryCommand.getPage(), contractQueryCommand.getSize());
        List<ContractVO> contracts = this.contractRepository.query(contractQueryCommand);
        PageInfo<ContractVO> pageInfo = new PageInfo<>(contracts);
        return new Paging<>((int) pageInfo.getTotal(), contracts);
    }

    public Paging<TrafficUpdateRecord> findByContractId(QueryTrafficCommand command) {
        PageHelper.startPage(command.getPage(), command.getSize());
        List<TrafficUpdateRecord> trafficUpdateRecordList = this.trafficUpdateRecordRepository.findByContractId(command);
        PageInfo<TrafficUpdateRecord> recordPageInfo = new PageInfo<>(trafficUpdateRecordList);
        return new Paging<>((int) recordPageInfo.getTotal(), trafficUpdateRecordList);
    }

    public void cancelContract(int userId, int contractId) {
        Contract contract = this.getById(contractId);
        contract.setExpireTime(new Date());
        if (contract.getStatus() == Contract.EFFECTIVE) {
            contract.setStatus(Contract.UN_EFFECTIVE);
        } else {
            contract.setStatus(Contract.EFFECTIVE);
        }
        contract.setModifier(userId);
        contract.setModifyTime(new Date());
        this.contractRepository.updateByPrimaryKey(contract);
    }

    public Integer findContractIdByDomain(String domain) {
        return cdnDomainRepository.findContractId(domain);
    }


    public ContractVO getContactVO(int id) {
        return this.contractRepository.fullSelect(id);
    }

    public Paging<ContractUsingVO> queryContractUsingRecords(int start, int limit, int userId, Boolean expired, Integer status) {
        Map<String, Object> params = new HashMap<>(8);
        params.put("start", start);
        params.put("expired", expired);
        params.put("limit", limit);
        params.put("status", status);
        params.put("userId", userId);
        List<ContractUsingVO> list = contractRepository.queryContractUsingDetail(params);
        int count = contractRepository.countContractUsingDetail(params);
        return new Paging<>(count, list);
    }
}
